package com.flsldl.mydb.backend.tm;

import com.flsldl.mydb.backend.utils.Panic;
import com.flsldl.mydb.backend.utils.Parser;
import com.flsldl.mydb.common.Error;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassName TransactionManagerImpl
 * @Description TODO
 * @Author @O_o @Date 2023/2/14 下午 8:43
 */
public class TransactionManagerImpl implements TransactionManager{

    // XID 文件头长度
    static final int LEN_XID_HEADER_LENGTH = 8;

    // 每个事务的占用长度
    private static final int XID_FIELD_SIZE = 1;

    // 事务的三种状态
    private static final byte FIELD_TRAN_ACTIVE = 0;
    private static final byte FIELD_TRAN_COMMITTED = 1;
    private static final byte FIELD_TRAN_ABORTED = 2;

    // 超级事务，永远为 commited 状态
    public static final long SUPER_XID = 0;

    // XID 文件后缀
    static final String XID_SUFFIX = ".xid";

    private RandomAccessFile file;
    private FileChannel fc;
    private long xidCounter;
    private Lock counterLock;

    TransactionManagerImpl(RandomAccessFile raf, FileChannel fc) {
        this.file = raf;
        this.fc = fc;
        counterLock = new ReentrantLock();
        checkXIDCounter();
    }

    // ----------------------------------(0v0)--> 分割线--------------------------------------------------------------

    /**
     * 检查 XID 文件是否合法
     * 读取 XID_FILE_HEADER 中的 xidcounter，根据它计算文件的理论长度，对比实际长度
     */
    private void checkXIDCounter() {
        long fileLen = 0;

        // 读取 XID 文件长度。
        try {
            fileLen = file.length();
        } catch (IOException e1) {
            // 读取失败，报错！！
            Panic.panic(Error.BadXIDFileException);
        }
        // 文件实际长度和理论长度不一致， 报错！！
        if (fileLen < LEN_XID_HEADER_LENGTH) {
            Panic.panic(Error.BadXIDFileException);
        }

        // 分配字节缓冲区
        // 参数为 HEN_XID_HEADER_LENGTH
        ByteBuffer buffer = ByteBuffer.allocate(LEN_XID_HEADER_LENGTH);
        try {
            // 读取文件内容的开始位置
            fc.position(0);
            // 读取文件内容，存入到 buffer 缓存空间中 ==> 读取 XID 文件的文件头
            fc.read(buffer);
        } catch (IOException e) {
            // 读取失败，报错！！
            Panic.panic(e);
        }
        // Parser.parseLong(buffer.array()); 将字节数组包装到新的缓冲区中，并返回缓冲区位置对应位置数据内容
        this.xidCounter = Parser.parseLong(buffer.array());
        // 根据文件头数据，找到文件最后一个事务的位置
        long end = getXidPosition(this.xidCounter + 1);
        // 文件实际长度和读取到的长度不一致，报错！！
        if (end != fileLen) {
            Panic.panic(Error.BadXIDFileException);
        }
    }
    // 根据事务 xid 取得其在 XID 文件中对应的位置
    private long getXidPosition(long xid) {
        return LEN_XID_HEADER_LENGTH + (xid - 1)*XID_FIELD_SIZE;
    }

    // ----------------------------------(0v0)--> 分割线--------------------------------------------------------------

    // 更新 xid 事务的状态为 status
    // status : 0 active 正在进行    1 committed 已提交    2 aborted 已撤销（回滚）
    private void updateXID(long xid, byte status) {
        // 获取 xid 事务在文件中的位置
        long offset = getXidPosition(xid);
        // 分配字节数组，并赋值
        byte[] tmp = new byte[XID_FIELD_SIZE];
        tmp[0] = status;
        // 为 tmp 分配缓存空间
        ByteBuffer buf = ByteBuffer.wrap(tmp);
        try {
            // 将 buf 数据写入文件的 offest 位置
            fc.position(offset);
            fc.write(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        try {
            /*
            * force(); 强制同步缓存内容到文件中
            * 参数为 boolean 类型，表示是否同步文件的元数据
            * */
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    // 将 xid 加一，并更新 XID Header
    private void incrXIDCounter() {
        // 增加一个事务
        xidCounter ++;
        // 分配缓存空间，将数据存入 buf
        ByteBuffer buf = ByteBuffer.wrap(Parser.long2Byte(xidCounter));
        // 修改 XID Header 更新事务个数
        try {
            fc.position(0);
            fc.write(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        try {
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    // 开始一个事务，并返回 xid
    public long begin() {
        // 加锁，防止同时开启事务时，事务总数出现错误
        counterLock.lock();
        try {
            long xid = xidCounter + 1;
            updateXID(xid, FIELD_TRAN_ACTIVE);
            incrXIDCounter();
            return xid;
        } finally {
            counterLock.unlock();
        }
    }

    // 提交 xid 事务
    public void commit(long xid) {
        updateXID(xid, FIELD_TRAN_COMMITTED);
    }

    // 回滚 xid 事务
    public void abort(long xid) {
        updateXID(xid, FIELD_TRAN_ABORTED);
    }

    // ----------------------------------(0v0)--> 分割线--------------------------------------------------------------

    // 检测 xid 事务是否处于 status 状态
    private boolean checkXID(long xid, byte status) {
        // 获取 xid 事务所在位置
        long offset = getXidPosition(xid);
        // 分配缓存空间
        ByteBuffer buf = ByteBuffer.wrap(new byte[XID_FIELD_SIZE]);
        try {
            // 读取事务状态，存入 buf
            fc.position(offset);
            fc.read(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        // 校对事务状态是否一致
        return buf.array()[0] == status;
    }

    public boolean isActive(long xid) {
        if (xid == SUPER_XID) return false;
        return checkXID(xid, FIELD_TRAN_ACTIVE);
    }
    public boolean isCommitted(long xid) {
        if (xid == SUPER_XID) return true;
        return checkXID(xid, FIELD_TRAN_COMMITTED);
    }
    public boolean isAborted(long xid) {
        if (xid == SUPER_XID) return false;
        return checkXID(xid, FIELD_TRAN_ABORTED);
    }

    public void close() {
        try {
            fc.close();
            file.close();
        } catch (IOException e) {
            Panic.panic(e);
        }
    }
}
